package mysqlrym

import (
	"errors"
	"fmt"
	"gitee.com/manoshi/go-util/conf"
	"gitee.com/manoshi/go-util/log"
	"gitee.com/manoshi/go-util/util"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"sync"
)

var customerDb *gorm.DB

var customerDbMap sync.Map

var gidNeedTX sync.Map

func SetTx() {
	gid := util.GetGID()
	gidNeedTX.Store(gid, 1)
}

func GetDb() *gorm.DB {
	return getDb(customerDb, &customerDbMap)
}

func getDb(db *gorm.DB, dbMap *sync.Map) *gorm.DB {
	gid := util.GetGID()
	_, needTx := gidNeedTX.Load(gid)
	if needTx {
		cacheTx, ok := dbMap.Load(gid)
		if ok {
			return cacheTx.(*gorm.DB)
		} else {
			if conf.LogShowSql {
				db = db.Debug()
			}
			tx := db.Begin()
			dbMap.Store(gid, tx)
			return tx
		}
	} else {
		if conf.LogShowSql {
			db = db.Debug()
		}
		return db
	}
}

func Rollback() {
	gid := util.GetGID()
	_, needTx := gidNeedTX.Load(gid)
	if needTx {
		rollbackTx(gid, &customerDbMap)
		gidNeedTX.Delete(gid)
	}

}

func Commit() {
	gid := util.GetGID()
	_, needTx := gidNeedTX.Load(gid)
	if needTx {
		commitTx(gid, &customerDbMap)
		gidNeedTX.Delete(gid)
	}

}

func rollbackTx(gid uint64, dbMap *sync.Map) {
	cacheTx, ok := dbMap.Load(gid)
	if ok {
		tx := cacheTx.(*gorm.DB)
		tx.Rollback()
		dbMap.Delete(gid)
	}
}

func commitTx(gid uint64, dbMap *sync.Map) {
	cacheTx, ok := dbMap.Load(gid)
	if ok {
		tx := cacheTx.(*gorm.DB)
		tx.Commit()
		dbMap.Delete(gid)
	}
}

func Init() {
	var err error
	customerDb, err = gorm.Open(mysql.Open(fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local",
		conf.MySql.User, conf.MySql.Password, conf.MySql.Host, conf.MySql.Port, conf.MySql.NameSpace)), &gorm.Config{
		PrepareStmt: true,
		Logger:      &log.CustomLogger{},
	})
	if err != nil {
		log.Error(fmt.Sprintf("%s connect error：%s", "Mysql server", err.Error()))
	} else {
		sqlDB, err := customerDb.DB()
		if conf.MySql.MaxIdleConns == 0 {
			sqlDB.SetMaxIdleConns(10)
		} else {
			sqlDB.SetMaxIdleConns(conf.MySql.MaxIdleConns)
		}
		if conf.MySql.MaxOpenConns == 0 {
			sqlDB.SetMaxOpenConns(10)
		} else {
			sqlDB.SetMaxOpenConns(conf.MySql.MaxOpenConns)
		}
		if err != nil {
			log.Error(fmt.Sprintf("%s connect error：%s", "Mysql server", err.Error()))
		} else {
			log.Info(fmt.Sprintf("%s connect success", "Mysql server"))
		}
	}

}

// DealTransaction 进行事务处理。注意使用此方法时需要检测内部的dao使用的是不是mysql.GetMyDb()
func DealTransaction(fc func()) (txErr error) {
	goID := util.GetGID()
	tx := GetDb().Begin()
	gidNeedTX.Store(goID, tx)
	defer func() {
		err1 := recover()
		if err1 != nil {
			tx.Rollback()
			switch err1.(type) {
			case error:
				txErr = (err1).(error)
				break
			default:
				txErr = errors.New(util.GetInterfaceToString(err1))
			}
		}
		gidNeedTX.Delete(goID)
	}()

	// 业务处理
	fc()

	err2 := tx.Commit().Error
	if err2 != nil {
		panic(err2)
	}
	return txErr
}
